import hmac
import json
import requests
import urllib.parse as urlparse
import hashlib
import sys
import time

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum


class Side(Enum):
  BUY = "Buy"
  SELL = "Sell"


class QtyType(Enum):
  BY_BASE = "ByBase"
  BY_QUOTE = "ByQuote"


class OrderType(Enum):
  MARKET = "Market"
  LIMIT = "Limit"
  STOP = "Stop"
  STOP_LIMIT = "StopLimit"
  MARKET_IF_TOUCHED = "MarketIfTouched"
  LIMIT_IF_TOUCHED = "LimitIfTouched"


class TimeInForce(Enum):
  GOOD_TILL_CANCEL = "GoodTillCancel"
  POST_ONLY = "PostOnly"
  IMMEDIATE_OR_CANCEL = "ImmediateOrCancel"
  FILL_OR_KILL = "FillOrKill"


class MoveOp(Enum):
  FUTURES_TO_SPOT = 1
  SPOT_TO_FUTURES = 2


class TargetPosMode(Enum):
  ONE_WAY = "OneWay"
  HEDGED = "Hedged"


class PhemexAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, path: str, query: str, expiry: str, body: str):
    assert path, "path must be not None and not empty!"
    assert expiry, "expiry must be not None and not empty!"

    before_signature = ''.join(filter(None, [path, query, expiry, body])).encode()
    mac = hmac.new(self._key.secret_key_bytes, before_signature, hashlib.sha256)
    return mac.hexdigest()

  def __call__(self, req: requests.PreparedRequest):
    # x-phemex-request-expiry : This describes the Unix EPoch SECONDS to expire the request,
    # normally it should be ( Now() + 1 minute)
    expiry = str(int(time.time() + 60))
    parsed = urlparse.urlparse(req.url)
    body = req.body and req.body.decode()

    signature = self._gen_signature(parsed.path, urlparse.unquote(parsed.query), expiry, body)

    req.headers.update({'x-phemex-access-token': self._key.access_key})
    req.headers.update({'x-phemex-request-expiry': expiry})
    req.headers.update({'x-phemex-request-signature': signature})
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req


class PhemexPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = PhemexAuth(key_path)
    self._url = "https://api.phemex.com"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_open_order(self, symbol: str):
    assert symbol, "symbol must be not None and not empty!"

    method = "GET"
    params = {
        'symbol': symbol,
    }
    path = "/spot/orders"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def convert(self, toCurrency: str, fromCurrency: str, fromAmountEv: str):
    assert toCurrency, "toCurrency must be not None and not empty!"
    assert fromCurrency, "fromCurrency must be not None and not empty!"
    assert fromAmountEv, "fromAmountEv must be not None and not empty!"

    method = "GET"
    params = {
        'toCurrency': toCurrency,
        'fromCurrency': fromCurrency,
        'fromAmountEv': fromAmountEv,
    }
    path = "/assets/quote"
    response = self._query.query(method=method, path=path, params=params)

    method = "POST"
    json = {
        'toCurrency': toCurrency,
        'fromCurrency': fromCurrency,
        'fromAmountEv': fromAmountEv,
        "code": response.json()['data']["code"],
    }
    path = "/assets/convert"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def transfer(self, amountEv: str, moveOp: MoveOp, currency: str):
    assert amountEv, "amountEv must be not None and not empty!"
    assert moveOp, "moveOp must be not None!"
    assert currency, "currency must be not None and not empty!"

    method = "POST"
    json = {
        'amountEv': amountEv,
        'moveOp': moveOp.value,
        'currency': currency,
    }
    path = "/assets/transfer"
    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def hedged_contract_position(self, currency: str):
    assert currency, "currency must be not None and not empty!"

    method = "GET"
    params = {
        'currency': currency,
    }
    path = "/g-accounts/accountPositions"
    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def switch_position_mode(self, symbol: str, target_pos_mode: TargetPosMode):
    assert symbol, "symbol must be not None and not empty!"
    assert target_pos_mode, "target_pos_mode must be not None!"

    method = "PUT"
    params = {
        'symbol': symbol,
        'targetPosMode': target_pos_mode.value,
    }
    path = "/g-positions/switch-pos-mode-sync"
    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def query_hedged_history_order(self, symbol: str):
    assert symbol, "symbol must be not None and not empty!"

    method = "GET"
    params = {
        'symbol': symbol,
    }
    path = "/api-data/g-futures/orders"
    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def contract_position(self, currency: str):
    assert currency, "currency must be not None and not empty!"

    method = "GET"
    json = {
        'currency': currency,
    }
    path = "/accounts/accountPositions"
    response = self._query.query(method=method, path=path, params=json)
    return response.json()

  def query_wallets(self, currency: str = None):
    method = "GET"
    params = {
        'currency': currency,
    }
    path = "/spot/wallets"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def cancel_order(self, symbol: str, order_id: str = None, cl_ord_id: str = None):
    assert symbol, "symbol must be not None and not empty!"
    one_and_only_one = (order_id == None and cl_ord_id != None) or (
        order_id != None and cl_ord_id == None)
    assert one_and_only_one, "order_id and cl_ord_id have one and only one!"

    method = "DELETE"
    params = {
        'symbol': symbol,
        'orderID': order_id,
        'clOrdID': cl_ord_id,
    }
    path = "/spot/orders"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def query_transfer(self, currency):
    method = "GET"

    params = {
        'currency': currency
    }

    path = "/assets/spots/sub-accounts/transfer"
    response = self._query.query(method=method, path=path, params=params)
    return response
    return response.json()

  def submit_order(
      self,
      symbol: str,
      side: Side,
      qty_type: QtyType,
      quote_qty_ev: str = None,
      base_qty_ev: str = None,
      price_ep: str = None,
      stop_px_ep: str = None,
      trigger: str = None,
      time_in_force: TimeInForce = None,
      ord_type: OrderType = None,
      cl_ord_id: str = None,
  ):
    assert symbol, "symbol must be not None and not empty!"
    assert side, "side must be not None!"
    assert qty_type, "qty_type must be not None!"

    method = "POST"
    json = {
        'symbol': symbol,
        'side': side.value,
        'qtyType': qty_type.value,
        'quoteQtyEv': quote_qty_ev,
        'baseQtyEv': base_qty_ev,
        'priceEp': price_ep,
        'stopPxEp': stop_px_ep,
        'trigger': trigger,
        'timeInForce': time_in_force and time_in_force.value,
        'ordType': ord_type and ord_type.value,
        'clOrdID': cl_ord_id,
    }
    path = "/spot/orders"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def query_withdraw(self):
    method = "GET"
    path = "/phemex-withdraw/wallets/api/withdrawHist"

    response = self._query.query(method=method, path=path)
    return response.json()

  def query_deposit(self):
    method = "GET"
    path = "/phemex-deposit/wallets/api/depositHist"

    response = self._query.query(method=method, path=path)
    return response.json()

  def query_spot_account_transfer(self, currency):
    method = "GET"
    path = "/assets/spots/sub-accounts/transfer"
    params = {"currency": currency, "start": 1677136576000}
    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def query_futures_account_transfer(self, currency):
    method = "GET"
    path = "/assets/futures/sub-accounts/transfer"
    params = {"currency": currency, "start": 1677136576000}
    response = self._query.query(method=method, path=path, params=params)
    return response.json()


# api doc url
# https://github.com/phemex/phemex-api-docs

if __name__ == "__main__":
  key_path = sys.argv[1]

  phemex = PhemexPrivateClient(key_path)
  info = phemex.query_deposit()
  # info = phemex.query_withdraw()
  # info = phemex.query_spot_account_transfer("USDT")
  # info = phemex.query_futures_account_transfer("USDT")
  # info = phemex.query_wallets()
  # info = phemex.cancel_order("sBTCUSDT", cl_ord_id="123")
  # info = phemex.submit_order("sBTCUSDT", Side.SELL, QtyType.BY_BASE, base_qty_ev="10000", price_ep="2100000000000", time_in_force=TimeInForce.GOOD_TILL_CANCEL, ord_type=OrderType.LIMIT)
  print(json.dumps(info, indent=2))
